import { ActorMessageDispatcherComponent, ActorMessageDispatcherInfo } from "../../../../Model/Server/Module/Actor/ActorMessageDispatcherComponent";
import { ObjectSystemAttribute, FriendOf, ExpandOf, Type, GetTypeOf, GetCustomAttributes, ActorMessageHandlerAttribute, ActorMessageHandlerAttrData } from "../../../../../Decorator/Decorator";
import { AwakeSystem } from "../../../../../Core/Module/EventSystem/IAwakeSystem";
import { DestroySystem } from "../../../../../Core/Module/EventSystem/IDestroySystem";
import { LoadSystem } from "../../../../../Core/Module/EventSystem/ILoadSystem";
import { Entity } from "../../../../../Core/Module/Entity/Entity";
import { Exception } from "../../../../../Core/Object/Exception";
import { List } from "../../../../../Support/Share/Lib/Collections/List";
import { EventSystem } from "../../../../../Core/Module/EventSystem/EventSystem";
import { IMActorHandler } from "../../../../Model/Server/Module/Actor/IMActorHandler";
import { OpcodeTypeComponent } from "../../../../Model/Share/Module/Message/OpcodeTypeComponent";
import { SceneType } from "../../../../../Core/Module/Entity/SceneType";

/**
 * 使用 Expand 扩展方法
 * 
 * 必须作出以下申明，才能调用对象的扩展方法
 */
declare module "../../../../Model/Server/Module/Actor/ActorMessageDispatcherComponent" {

    interface ActorMessageDispatcherComponent {
        Awake(): void;
        Load(): void;
        RegisterHandler(type: Type, handler: ActorMessageDispatcherInfo): void;
        Handle(entity: Entity, fromProcess: number, message: Object): Promise<any>;
    }

}

@ExpandOf(ActorMessageDispatcherComponent)
export class ActorMessageDispatcherComponentSystem {

    private static Awake(self: ActorMessageDispatcherComponent): void {
        self.Load();
    }

    private static Load(self: ActorMessageDispatcherComponent): void {
        self.ActorMessageHandlers.Clear();

        let types = EventSystem.Instance.GetTypes(GetTypeOf(ActorMessageHandlerAttribute));
        for (let type in types) {
            let clazz = types[type];
            let obj = new clazz();
            let imActorHandler = obj as IMActorHandler;
            if (imActorHandler == null) {
                throw new Exception(`message handler not inherit IMActorHandler abstract class: ${GetTypeOf(obj)}`);
            }
            let attrs = GetCustomAttributes(clazz);
            for (let attr of attrs) {
                let actorMessageHandlerAttribute = attr as ActorMessageHandlerAttrData;
                let imActorHandlerParent = Object.getPrototypeOf(imActorHandler) as IMActorHandler;
                let messageType = imActorHandlerParent.GetRequestType();
                let handleResponseType = imActorHandlerParent.GetResponseType();
                if (handleResponseType != null) {
                    let responseType = OpcodeTypeComponent.Instance.GetResponseType(messageType);
                    if (handleResponseType != responseType) {
                        throw new Exception(`message handler response type error: ${messageType}`);
                    }
                }
                let actorMessageDispatcherInfo = new ActorMessageDispatcherInfo(actorMessageHandlerAttribute.SceneType, imActorHandler);
                self.RegisterHandler(messageType, actorMessageDispatcherInfo);
            }
        }
    }

    private static RegisterHandler(self: ActorMessageDispatcherComponent, type: Type, handler: ActorMessageDispatcherInfo): void {
        if (!self.ActorMessageHandlers.ContainsKey(type)) {
            self.ActorMessageHandlers.Add(type, new List<ActorMessageDispatcherInfo>());
        }
        self.ActorMessageHandlers.TryGetValue(type).Add(handler);
    }

    private static async Handle(self: ActorMessageDispatcherComponent, entity: Entity, fromProcess: number, message: Object): Promise<any> {
        let list = self.ActorMessageHandlers.TryGetValue(GetTypeOf(message));
        if (!list) {
            throw new Exception(`not found message handler: {message}`);
        }
        let sceneType = entity.DomainScene().SceneType;
        for (let key in list.ToArray()) {
            let index = parseInt(key);
            let actorMessageDispatcherInfo = list.Get(index);
            if (actorMessageDispatcherInfo.SceneType != sceneType && actorMessageDispatcherInfo.SceneType != SceneType.All) {
                continue;
            }
            await actorMessageDispatcherInfo.IMActorHandler.Handle(entity, fromProcess, message);
        }
    }
}

@FriendOf(ActorMessageDispatcherComponent)
@ObjectSystemAttribute()
export class ActorMessageDispatcherComponentAwakeSystem extends AwakeSystem<ActorMessageDispatcherComponent> {

    Awake(self: ActorMessageDispatcherComponent): void {
        ActorMessageDispatcherComponent.Instance = self;
        self.Awake();
    }

}

@FriendOf(ActorMessageDispatcherComponent)
@ObjectSystemAttribute()
export class ActorMessageDispatcherComponentDestroySystem extends DestroySystem<ActorMessageDispatcherComponent> {

    Destroy(self: ActorMessageDispatcherComponent): void {
        self.ActorMessageHandlers.Clear();
        ActorMessageDispatcherComponent.Instance = null;
    }

}

@FriendOf(ActorMessageDispatcherComponent)
@ObjectSystemAttribute()
export class ActorMessageDispatcherComponentLoadSystem extends LoadSystem<ActorMessageDispatcherComponent> {

    Load(self: ActorMessageDispatcherComponent): void {
        self.Load();
    }

}